export default class RequestInstance {
  http: XMLHttpRequest | undefined;
  error?: (e: RequestInstance) => void;
  ok?: (e: RequestInstance) => void;
  cancel?: (e: RequestInstance) => void;
  process?: (e: RequestInstance) => void;
  uploadProcess?: (e: RequestInstance) => void;
  reSendRequest?: () => void;
  body: File;
  uploadSize?: number;
  size?: number;
  state?: 'ok' | 'fail' | 'process' | 'init' | 'cancel' | 'uploadProcess';
  request?: XMLHttpRequest;
  constructor({
    body,
    process,
    uploadProcess,
    error,
    ok,
    cancel,
  }: {
    body?: File;
    process?: () => void;
    uploadProcess?: () => void;
    error?: () => void;
    ok?: () => void;
    cancel?: () => void;
  }) {
    this.uploadSize = 0;
    this.size = 0;
    this.body = body;
    this.state = 'init'; //ok 上传完成 fail 上传失败 process 正在上传 init 还没开始上传 cancel 取消上传 uploadProcess 请求下载过程
    this.error = error;
    this.ok = ok;
    this.cancel = cancel;
    this.uploadProcess = uploadProcess;
    this.process = process;
    this.init();
  }
  init() {
    this.http = new XMLHttpRequest();
    this.http.onloadstart = () => {
      // 刚接收到数据
    };
    this.http.onabort = () => {
      // 请求关闭
    };
    this.http.onloadend = () => {
      // 请求结束
    };
    this.state = 'init';
    this.http.onerror = (): void => this.handleError(); //请求失败
    this.http.upload.onprogress = (e): void => {
      // 上传过程
      this.uploadSize = Number(((e.loaded / e.total) * 100).toFixed(0));
      this.state = 'uploadProcess';
      this.uploadProcess?.(this);
    };
    this.http.onprogress = (e): void => {
      // 请求下载过程
      this.size = Number(((e.loaded / e.total) * 100).toFixed(0));
      this.state = 'process';
      this.process?.(this);
    };
  }
  handleError() {
    this.state = 'fail';
    this.error?.(this);
  }
  // 上传文件
  public upload({
    url,
    file,
    async = true,
  }: {
    url: string;
    file: FormData;
    async: boolean;
  }): Promise<any> {
    return new Promise((resolve, reject) => {
      // @ts-ignore
      this.http.onload = ({ target }: { target: XMLHttpRequest }) => {
        try {
          resolve(JSON.parse(target.response));
          this.state = 'ok';
          this.ok?.(this);
        } catch (err) {
          reject(target.response);
          this.handleError();
        }
      }; // 请求成功
      this.http?.open('POST', url, async);
      this.reSendRequest = () => this.http?.send(file);
      this.reSendRequest?.();
    });
  }
  // 发送请求
  public fetch({
    method,
    url,
    body,
    header,
    async = true,
  }: {
    method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
    url: string;
    body?: { [key: string]: any };
    header?: { [key: string]: string };
    async?: boolean;
  }): Promise<any> {
    return new Promise((resolve, reject) => {
      // @ts-ignore
      this.http.onload = ({ target }: { target: XMLHttpRequest }) => {
        try {
          resolve(JSON.parse(target.response));
          this.state = 'ok';
          this.ok?.(this);
        } catch (err) {
          reject(target.response);
          this.handleError();
        }
      }; // 请求成功
      this.http?.open(method, url, async);
      if (method === ('POST' || 'PUT' || 'PATCH')) {
        this.http?.setRequestHeader(
          // 设置setRequestHeader必须在open后send前
          'Content-Type',
          'application/x-www-form-urlencoded'
        );
      }
      if (header)
        // 设置请求头
        Object.keys(header).forEach((item) => {
          this.http?.setRequestHeader(item, header[item]);
        });
      this.reSendRequest = () =>
        this.http?.send(
          method === 'POST' || 'PUT' || 'PATCH'
            ? body
              ? Object.keys(body).reduce(
                  (total, item) => `${total}${item}=${body[item]}&`,
                  ''
                )
              : ''
            : JSON.stringify(body)
        );
      this.reSendRequest?.();
    });
  }
  public about() {
    // 取消请求
    this.http?.abort();
    this.state = 'cancel';
    this.cancel(this);
  }
  public reSend() {
    // 重发请求
    this.http?.abort();
    this.state = 'init';
    this.reSendRequest?.();
  }
}
